<template><div><h2 id="属性" tabindex="-1"><a class="header-anchor" href="#属性"><span>属性</span></a></h2>
<p>Yii 中文文档 /</p>
<h2 id="属性-properties" tabindex="-1"><a class="header-anchor" href="#属性-properties"><span>属性（Properties）</span></a></h2>
<p>在 PHP 中，类的成员变量也被称为 <em>属性</em>。它们是类定义的一部分，用来表现一个实例的状态（也就是区分类的不同实例）。在具体实践中，常常会想用一个稍微特殊些的方法实现属性的读写。例如，如果有需求每次都要对 <code v-pre>label</code> 属性执行 trim 操作，就可以用以下代码实现：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token variable">$object</span><span class="token operator">-></span><span class="token property">label</span> <span class="token operator">=</span> <span class="token function">trim</span><span class="token punctuation">(</span><span class="token variable">$label</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div></div></div><p>上述代码的缺点是只要修改 <code v-pre>label</code> 属性就必须再次调用 <code v-pre>trim()</code> 函数。若将来需要用其它方式处理 <code v-pre>label</code> 属性，比如首字母大写，就不得不修改所有给 <code v-pre>label</code> 属性赋值的代码。这种代码的重复会导致 bug，这种实践显然需要尽可能避免。</p>
<p>为解决该问题，Yii 引入了一个名为 <code v-pre>yii\base\BaseObject</code> 的基类，它支持基于类内的 <em>getter</em> 和 <em>setter</em>（读取器和设定器）方法来定义属性。如果某个类需要支持这个特性，只需要继承 [[yii\base\BaseObject]] 或其子类即可。</p>
<blockquote>
<p>Info: 几乎每个 Yii 框架的核心类都继承自 <code v-pre>yii\base\BaseObject</code> 或其子类。这意味着只要在核心类中见到 getter 或 setter 方法，就可以像调用属性一样调用它。</p>
</blockquote>
<p>getter 方法是名称以 <code v-pre>get</code> 开头的方法，而 setter 方法名以 <code v-pre>set</code> 开头。方法名中 <code v-pre>get</code> 或 <code v-pre>set</code> 后面的部分就定义了该属性的名字。如下面代码所示，getter 方法 <code v-pre>getLabel()</code> 和 setter 方法 <code v-pre>setLabel()</code> 操作的是 <code v-pre>label</code> 属性，示例如下：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token keyword">namespace</span> <span class="token package">app<span class="token punctuation">\</span>components</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">use</span> <span class="token package">yii<span class="token punctuation">\</span>base<span class="token punctuation">\</span>BaseObject</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">class</span> <span class="token class-name-definition class-name">Foo</span> <span class="token keyword">extends</span> <span class="token class-name">BaseObject</span></span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">    <span class="token keyword">private</span> <span class="token variable">$_label</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">getLabel</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token keyword">return</span> <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">_label</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">    <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function-definition function">setLabel</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token punctuation">{</span></span>
<span class="line">        <span class="token variable">$this</span><span class="token operator">-></span><span class="token property">_label</span> <span class="token operator">=</span> <span class="token function">trim</span><span class="token punctuation">(</span><span class="token variable">$value</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span>
<span class="line">    <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>（详细解释：getter 和 setter 方法创建了一个名为 <code v-pre>label</code> 的属性，在这个例子里，它指向一个私有的内部属性 <code v-pre>_label</code>。）</p>
<p>getter/setter 定义的属性用法与类成员变量一样。两者主要的区别是：当这种属性被读取时，对应的 getter 方法将被调用；而当属性被赋值时，对应的 setter 方法就调用。如：</p>
<div class="language-php line-numbers-mode" data-highlighter="prismjs" data-ext="php" data-title="php"><pre v-pre class="language-php"><code><span class="line"><span class="token comment">// 等效于 $label = $object->getLabel();</span></span>
<span class="line"><span class="token variable">$label</span> <span class="token operator">=</span> <span class="token variable">$object</span><span class="token operator">-></span><span class="token property">label</span><span class="token punctuation">;</span></span>
<span class="line"></span>
<span class="line"><span class="token comment">// 等效于 $object->setLabel('abc');</span></span>
<span class="line"><span class="token variable">$object</span><span class="token operator">-></span><span class="token property">label</span> <span class="token operator">=</span> <span class="token string single-quoted-string">'abc'</span><span class="token punctuation">;</span></span>
<span class="line"></span></code></pre>
<div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>只定义了 getter 没有 setter 的属性是 <em>只读属性</em>。尝试赋值给这样的属性将导致 <code v-pre>yii\base\InvalidCallException</code> 无效调用异常。类似的，只有 setter 方法而没有 getter 方法定义的属性是 <em>只写属性</em>，尝试读取这种属性也会触发异常。使用只写属性的情况几乎没有。</p>
<p>通过 getter 和 setter 定义的属性也有一些特殊规则和限制：</p>
<ul>
<li>这类属性的名字是 <em>不区分大小写</em> 的。如，<code v-pre>$object-&gt;label</code> 和 <code v-pre>$object-&gt;Label</code> 是同一个属性。因为 PHP 方法名是不区分大小写的。</li>
<li>如果此类属性名和类成员变量相同，以后者为准。例如，假设以上 <code v-pre>Foo</code> 类有个 <code v-pre>label</code> 成员变量，然后给 <code v-pre>$object-&gt;label = 'abc'</code> 赋值，将赋给成员变量而不是 setter <code v-pre>setLabel()</code> 方法。</li>
<li>这类属性不支持可见性（访问限制）。定义属性的 getter 和 setter 方法是 public、protected 还是 private 对属性的可见性没有任何影响。</li>
<li>这类属性的 getter 和 setter 方法只能定义为<em>非静态</em>的，若定义为静态方法（static）则不会以相同方式处理。</li>
<li>对不确定有无魔术方法（getter 或 setter）的属性正常调用 <code v-pre>property_exists()</code> 将不会生效。你应该分别调用 [[yii\base\BaseObject::canGetProperty()]] 或 [[yii\base\BaseObject::canSetProperty()]] 。</li>
</ul>
<p>回到开头提到的问题，与其处处要调用 <code v-pre>trim()</code> 函数，现在我们只需在 setter <code v-pre>setLabel()</code> 方法内调用一次。如果 label 首字母变成大写的新要求来了，我们只需要修改<code v-pre>setLabel()</code> 方法，而无须接触任何其它代码。</p>
<blockquote>
<p>💖喜欢本文档的，欢迎点赞、收藏、留言或转发，谢谢支持！<br>
作者邮箱：zhuzixian520@126.com，github地址：<a href="https://github.com/zhuzixian520" target="_blank" rel="noopener noreferrer">github.com/zhuzixian520</a></p>
</blockquote>
</div></template>


